#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <assert.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <SDL2/SDL.h>

#include "bsp.h"
#include "key.h"
#include "gpio.h"
#include "event.h"
#include "log2file.h"

void gpio_init(void)
{
    gpio_t *gpio_key1, *gpio_key2, *gpio_key3, *gpio_key4, *gpio_key5;
    gpio_key1 = gpio_new();
    gpio_key2 = gpio_new();
    gpio_key3 = gpio_new();
    gpio_key4 = gpio_new();
    gpio_key5 = gpio_new();

    gpio_open_sysfs(gpio_key1, GPIO_KEY1, GPIO_DIR_IN);
    gpio_open_sysfs(gpio_key2, GPIO_KEY2, GPIO_DIR_IN);
    gpio_open_sysfs(gpio_key3, GPIO_KEY3, GPIO_DIR_IN);
    gpio_open_sysfs(gpio_key4, GPIO_KEY4, GPIO_DIR_IN);
    gpio_open_sysfs(gpio_key5, GPIO_KEY5, GPIO_DIR_IN);
    gpio_free(gpio_key1);
    gpio_free(gpio_key2);
    gpio_free(gpio_key3);
    gpio_free(gpio_key4);
    gpio_free(gpio_key5);
}

static void pushkey(eKeyVal key)
{
    SDL_Event event;
    SDL_zero(event);
    event.type = SDL_KEYUP;
    event.key.keysym.sym = key;
    SDL_PushEvent(&event);
}

static int shortkey_val(unsigned long val)
{
    int key;
    switch (KEY_MASK & val)
    {
    case KEY_PRESS(KEY4) & KEY_PRESS(KEY5):
        key = KEY4_KEY5_SHORT;
        break;
    case KEY_PRESS(KEY1):
        key = KEY1_SHORT;
        break;
    case KEY_PRESS(KEY2):
        key = KEY2_SHORT;
        break;
    case KEY_PRESS(KEY3):
        key = KEY3_SHORT;
        break;
    case KEY_PRESS(KEY4):
        key = KEY4_SHORT;
        break;
    case KEY_PRESS(KEY5):
        key = KEY5_SHORT;
        break;
    default:
        printf("shortkey_val error!\n");
        key = NO_KEY;
        break;
    }
    return key;
}

static int longkey_val(unsigned long val)
{
    int key;
    switch (KEY_MASK & val)
    {
    case KEY_PRESS(KEY2) & KEY_PRESS(KEY3):
        key = KEY2_KEY3_LONG;
        break;
    case KEY_PRESS(KEY4) & KEY_PRESS(KEY5):
        key = KEY4_KEY5_LONG;
        break;
    case KEY_PRESS(KEY1):
        key = KEY1_LONG;
        break;
    case KEY_PRESS(KEY2):
        key = KEY2_LONG;
        break;
    case KEY_PRESS(KEY3):
        key = KEY3_LONG;
        break;
    case KEY_PRESS(KEY4):
        key = KEY4_LONG;
        break;
    case KEY_PRESS(KEY5):
        key = KEY5_LONG;
        break;
    default:
        printf("longkey_val error!\n");
        key = NO_KEY;
        break;
    }
    return key;
}

static int long_cont_key_val(unsigned long val)
{
    int key;
    switch (KEY_MASK & val)
    {
    case KEY_PRESS(KEY2):
        key = KEY2_LONG_CONT;
        break;
    case KEY_PRESS(KEY3):
        key = KEY3_LONG_CONT;
        break;
    case KEY_PRESS(KEY4):
        key = KEY4_LONG_CONT;
        break;
    case KEY_PRESS(KEY5):
        key = KEY5_LONG_CONT;
        break;
    default:
        printf("long_cont_key_val error!\n");
        key = NO_KEY;
        break;
    }
    return key;
}

static int Keypressed(unsigned long *pval)
{
    unsigned long val = 0;
    // read data in
    devmem_read(VIENNA_GPIOC_0_MMR_BASE + GPIOC_DATA_IN, &val);
    if((val & KEY_MASK) != KEY_MASK) {
        (*pval) = (val & KEY_MASK);
        return 1;
    } else {
        return 0;
    }
}

//函数原型：
//	long clock_gettime (clockid_t which_clock, struct timespec *tp);
//参数列表：
//	CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户该成其他,则对应的时间相应改变。
//	CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
//	CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
//	CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间
//返回值：
//	0:成功，-1:错误，在errno中保存错误代码
//目的：测代码运行时间
static void diff(struct timespec *start, struct timespec *end, struct timespec *interv)
{
    if((end->tv_nsec - start->tv_nsec) < 0) {
        interv->tv_sec = end->tv_sec - start->tv_sec - 1;
        interv->tv_nsec = 1000000000 + end->tv_nsec - start->tv_nsec;
    } else {
        interv->tv_sec = end->tv_sec - start->tv_sec;
        interv->tv_nsec = end->tv_nsec - start->tv_nsec;
    }
}

static void ticks(void)
{
    static struct timespec gTick_start = {0};
    struct timespec end, interv;

    clock_gettime(CLOCK_MONOTONIC, &end);
    diff(&gTick_start, &end, &interv);
    if(interv.tv_nsec / 1000000 >= 100) {
        gTick_start = end;
    }
}

static void *thread_key_func(void *p)
{
    int key_cnt = 0, mKeyVal = 0, long_key_press = 0;
    int timeout_30s_cnt = 0;

    pthread_detach(pthread_self());

    while(onExit())
    {
        unsigned long val1 = 0xFFFF;
        unsigned long val2 = 0xFFFF;

        long_key_press = 0;
        ticks();

        while(Keypressed(&val1)) {
            if(val1 < val2) {
                val2 = val1;
                key_cnt = 0;
            }
            usleep(10 * 1000);
            key_cnt ++;

            if(60 == key_cnt) {
                mKeyVal = longkey_val(val2);
                pushkey(mKeyVal);
                long_key_press = 1;
                key_cnt = 80;
            }
            if (80 == key_cnt) {
				mKeyVal = long_cont_key_val(val2);
				pushkey(mKeyVal);
				long_key_press = 1;
				key_cnt = 60;
            }
            
            timeout_30s_cnt = 0;
            ticks();
        }

        if((key_cnt > 3) && (0 == long_key_press)) {
            mKeyVal = shortkey_val(val2);
            pushkey(mKeyVal);
            key_cnt = 0;
        } else {
            usleep(100 * 1000);
            timeout_30s_cnt++;

            if(timeout_30s_cnt > 30 * 10) {
                mKeyVal = KEY_TIMEOUT_30S;
                pushkey(mKeyVal);
                timeout_30s_cnt = 0;
            }
        }
    }
    
    return NULL;
}

int create_keys_thread(void)
{
    pthread_t thread_key;
    if(-1 == pthread_create(&thread_key, NULL, thread_key_func, NULL)) {
        perror("Create thread of gpio key");
        return -1;
    }
    return 0;
}

#define GPIO_IRCUT_ENB 65
#define GPIO_IRCUT_FBC 66
gpio_t *gpio_ircut_enb, *gpio_ircut_fbc;
void IRCUT_modle(int modle)
{
	gpio_write(gpio_ircut_enb, 0);
	usleep(50000);
	if(modle == 0) {
        gpio_write(gpio_ircut_fbc, 0);
    } else {
        gpio_write(gpio_ircut_fbc, 1);
    }
	usleep(50000);
	gpio_write(gpio_ircut_enb, 1);
}

void IRCUT_init(int initial_modle)
{
	// IR CUT 先设置控制电平，再使能，否则引起IRCUT误操作
    gpio_ircut_fbc = gpio_new();
    gpio_open_sysfs(gpio_ircut_fbc, GPIO_IRCUT_FBC, GPIO_DIR_OUT);

	if(initial_modle == 0) {
        gpio_write(gpio_ircut_fbc, 1);
    } else {
        gpio_write(gpio_ircut_fbc, 0);
    }

    gpio_ircut_enb = gpio_new();
    gpio_open_sysfs(gpio_ircut_enb, GPIO_IRCUT_ENB, GPIO_DIR_OUT);
	gpio_write(gpio_ircut_enb, 0);
	usleep(50000);
	gpio_write(gpio_ircut_enb, 1);
}